package com.hup.utils.swing.customView;

import com.hup.utils.commons.TextUtil;
import com.hup.utils.swing.ComponentUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * 监听键盘快捷键控件;
 * .有光标效果
 * .删除键清空内容
 * .支持快捷键捕获: [CTRL,ALT,SHIFT,WINDOW]+?
 * .若快捷键被拦截,可以重按单个按键(如 WIN+D分开按)
 * .支持同时按下多个非功能键(如[左+上箭头,W+A]),
 *
 * @author hugan
 * @date 2020/10/26
 */
@Log4j2
public class KeyboardText extends NotInputTextView {
    /**
     * 被按下的按键
     */
    @Getter
    private final ArrayList<Integer> lstKeyCode = new ArrayList<>();
    /**
     * 当前被按下的按键,不含 {@link #isMyModifier(int)};
     * 实现[支持同时按下多个非功能键]
     */
    private final HashSet<Integer> setPressingKey = new HashSet<>();
    @Setter
    private KeyChangedListener keyChangedListener;
    @Setter
    @Getter
    private String hint = "请按下按键";

    public KeyboardText() {
        setToolTipText("若快捷键被拦截,可以重按单个按键");
    }

    @Override
    protected void processKeyEvent(KeyEvent e) {
        e.consume();
        if (e.getID() == KeyEvent.KEY_RELEASED) {
            setPressingKey.remove(e.getKeyCode());
            //log.info("setPressingKey={}", setPressingKey);
            return;
        }
        if (e.getID() != KeyEvent.KEY_PRESSED) return;

        //删除键:有内容时删除内容,没内容就设置按键
        if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE && !lstKeyCode.isEmpty()) {
            lstKeyCode.clear();
        } else {
            handleKeyPressed(e);
        }
        updateTextByCodes();
        if (keyChangedListener != null) keyChangedListener.onKeyChanged();
    }

    private void handleKeyPressed(KeyEvent e) {
        //log.info("e={}, {}", e.getKeyCode(), KeyEvent.getKeyText(e.getKeyCode()));
        //int keyLocation = e.getKeyLocation();//按键位置java.awt.event.KeyEvent.paramString
        int curKeyCode = e.getKeyCode();
        boolean isCurMyModifier = isMyModifier(curKeyCode);
        if (!isCurMyModifier) setPressingKey.add(curKeyCode);

        boolean isEndByMyModifier = lstKeyCode.isEmpty() || isMyModifier(lstKeyCode.get(lstKeyCode.size() - 1));//以MyModifier结尾
        if (isEndByMyModifier) {
            //前面是modifier,按需加入
            if (!lstKeyCode.contains(curKeyCode)) lstKeyCode.add(curKeyCode);
        } else {
            //前面不是modifier
            if (isCurMyModifier) {
                //只保留当前按键
                lstKeyCode.clear();
                lstKeyCode.add(curKeyCode);
            } else {
                //移除isMyModifier后的key
                for (int i = lstKeyCode.size() - 1; i >= 0; i--) {
                    if (isMyModifier(lstKeyCode.get(i))) break;
                    lstKeyCode.remove(i);
                }
                //添加[当前按下中的key]
                for (Integer pressing : setPressingKey) if (!lstKeyCode.contains(pressing)) lstKeyCode.add(pressing);
            }
        }
    }

    private void updateTextByCodes() {
        if (lstKeyCode.isEmpty()) {
            setText(null);
            ComponentUtil.afterChangedChild(this.getParent());
            return;
        }
        setText(TextUtil.getText(lstKeyCode, (sb1, hasNext, next) -> {
            sb1.append(KeyEvent.getKeyText(next));
            if (hasNext) sb1.append('+');
        }));
        ComponentUtil.afterChangedChild(this.getParent());
    }

    public void setKeyCode(List<Integer> lstKeyCode, boolean notifyListener) {
        this.lstKeyCode.clear();
        if (lstKeyCode != null) this.lstKeyCode.addAll(lstKeyCode);
        updateTextByCodes();
        if (notifyListener && keyChangedListener != null) keyChangedListener.onKeyChanged();
    }

    /**
     * @return 是否为本程序定义的modifiers
     */
    private static boolean isMyModifier(int keyCode) {
        return keyCode == KeyEvent.VK_WINDOWS || keyCode == KeyEvent.VK_CONTROL
                || keyCode == KeyEvent.VK_ALT || keyCode == KeyEvent.VK_SHIFT;
    }

    public interface KeyChangedListener {
        void onKeyChanged();
    }

}
